package com.lemon.cloud.mp.config;

import cn.hutool.core.collection.CollectionUtil;
import com.google.common.collect.Maps;
import com.lemon.cloud.comm.constants.CacheConstants;
import com.lemon.cloud.comm.constants.CommonConstants;
import com.lemon.cloud.mp.entity.WxAccount;
import com.lemon.cloud.mp.handler.*;
import com.lemon.cloud.mp.service.WxAccountService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.impl.WxMpRedissonConfigImpl;
import me.chanjar.weixin.mp.constant.WxMpEventConstants;
import org.redisson.api.RedissonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;

import jakarta.annotation.PostConstruct;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 微信公众号，账号初始化
 */
@Slf4j
@Configuration
@RequiredArgsConstructor
public class WxMpInitConfigService {

    private final WxAccountService accountService;

    private final RedissonClient redissonClient;
    /**
     * 保存 appid-mpservice 的对应关系
     */
    private static Map<String, WxMpService> mpServices = Maps.newHashMap();

    /**
     * 保存 appid-router 的对应关系
     */
    private static Map<String, WxMpMessageRouter> routers = Maps.newHashMap();

    private final LogHandler logHandler;

    private final NullHandler nullHandler;

    private final KfSessionHandler kfSessionHandler;

    private final StoreCheckNotifyHandler storeCheckNotifyHandler;

    private final LocationHandler locationHandler;

    private final MenuHandler menuHandler;

    private final MsgHandler msgHandler;

    private final UnsubscribeHandler unsubscribeHandler;

    private final SubscribeHandler subscribeHandler;

    private final ScanHandler scanHandler;

    @PostConstruct
    public void init() {
        log.info("start init mp config......");

        List<WxAccount> accountList = accountService.list();
        if (CollectionUtil.isEmpty(accountList)) {
            return;
        }

        mpServices = accountList.stream().map(a -> {
            WxMpRedissonConfigImpl configStorage = new WxMpRedissonConfigImpl(redissonClient, CommonConstants.BACK_END_PROJECT);
            configStorage.setAppId(a.getAppid());
            configStorage.setSecret(a.getAppsecret());
            configStorage.setToken(a.getToken());
            configStorage.setAesKey(a.getAeskey());
            WxMpService service = new WxMpServiceImpl();
            service.setWxMpConfigStorage(configStorage);
            routers.put(a.getAppid(), this.newRouter(service));
            return service;
        }).collect(Collectors.toMap(s -> s.getWxMpConfigStorage().getAppId(), a -> a));

        log.info("end init mp config......");
    }

    private WxMpMessageRouter newRouter(WxMpService wxMpService) {
        final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);

        // 记录所有事件的日志 （异步执行）
        newRouter.rule().handler(this.logHandler).next();

        // 接收客服会话管理事件
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxMpEventConstants.CustomerService.KF_CREATE_SESSION).handler(this.kfSessionHandler).end();
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxMpEventConstants.CustomerService.KF_CLOSE_SESSION).handler(this.kfSessionHandler).end();
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxMpEventConstants.CustomerService.KF_SWITCH_SESSION).handler(this.kfSessionHandler).end();

        // 门店审核事件
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT).event(WxMpEventConstants.POI_CHECK_NOTIFY)
                .handler(this.storeCheckNotifyHandler).end();

        // 自定义菜单事件
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT).event(WxConsts.MenuButtonType.CLICK).handler(this.menuHandler)
                .end();

        // 点击菜单连接事件
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT).event(WxConsts.MenuButtonType.VIEW).handler(this.nullHandler)
                .end();

        // 关注事件
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT).event(WxConsts.EventType.SUBSCRIBE)
                .handler(this.subscribeHandler).end();

        // 取消关注事件
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT).event(WxConsts.EventType.UNSUBSCRIBE)
                .handler(this.unsubscribeHandler).end();

        // 上报地理位置事件
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT).event(WxConsts.EventType.LOCATION).handler(this.locationHandler)
                .end();

        // 接收地理位置消息
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION).handler(this.locationHandler).end();

        // 扫码事件
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT).event(WxConsts.EventType.SCAN).handler(this.scanHandler).end();

        // 默认
        newRouter.rule().async(false).handler(this.msgHandler).end();

        return newRouter;
    }

    /**
     * redis 监听配置,监听 mp_redis_reload_topic,重新加载配置
     *
     * @param redisConnectionFactory redis 配置
     * @return
     */
    @Bean
    public RedisMessageListenerContainer redisContainer(RedisConnectionFactory redisConnectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(redisConnectionFactory);
        container.addMessageListener((message, bytes) -> {
            log.warn("接收到重新加载公众号配置事件");
            init();
        }, new ChannelTopic(CacheConstants.MP_REDIS_RELOAD_TOPIC));
        return container;
    }

    public static Map<String, WxMpMessageRouter> getRouters() {
        return routers;
    }

    public static Map<String, WxMpService> getMpServices() {
        return mpServices;
    }

}
